#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QDebug"
#include "QFileDialog"
#include "QtGui"
#include "QMessageBox"
#include "QTextCodec"
#include "mera.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include "graphitemobject.h" // our object

namespace Model { class GHModel; }

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{

    ui->setupUi(this);

    connect(ui->openFileAction, &QAction::triggered, this, &MainWindow::open_file_dialog);
    connect(ui->action_SQL, &QAction::triggered, this, &MainWindow::Dat_to_SQL);
    connect(ui->copyAction, &QAction::triggered, this, &MainWindow::to_clipboard);
    connect(ui->insertAction, &QAction::triggered, this, &MainWindow::from_clipboard);
    connect(ui->saveButton, &QPushButton::clicked, this, &MainWindow::save_to_file);
    connect(ui->polyButton, &QPushButton::clicked, this, &MainWindow::Visual_poly);
    connect(ui->teleButton, &QPushButton::clicked, this, &MainWindow::Visual_mera);
    connect(ui->polynomButton, &QPushButton::clicked, this, &MainWindow::boofer_to_polynom);
    connect(ui->addInButton, &QPushButton::clicked, this, &MainWindow::add_data_line);
    connect(ui->deletInButton, &QPushButton::clicked, this, &MainWindow::remove_data_line);
    connect(ui->infAction, &QAction::triggered, this, &MainWindow::open_info);
    connect(ui->treeView, &QAbstractItemView::doubleClicked, this, &MainWindow::open_file_tree);
    connect(ui->filter_box, &QComboBox::currentTextChanged, this, &MainWindow::change_filter_file_systemModel);
    ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
    apidecoding = new ApiDecoding;
    build_file_system_model();
    build_GHmodel();
    make_test_data(10, 0.5, 5);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::Dat_to_SQL()
{
    MERA mera(0,"/home/student/alekseev_pfoject/alekseev_semen/test/test_R/test.mera");
    if(mera.load())
        if(mera.paramList().size()){
            qDebug() << mera.paramList().first()->open_only(QIODevice::ReadOnly);  //readDat
        }

    QString filePath = QFileDialog::getOpenFileName(this, tr("Open File"), "/home", tr("Text files (*.txt)"));
    if(filePath.isEmpty())
    {
        return;
    }
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QMessageBox::warning(this, "Внимание", "Файл не может быть открыт");
        return;
    }
    QTextStream out(&file);
    int values_count = mera.paramList().first()->unread_size() / sizeof(double);
    int group_count = 131000;
    int group_number = 0;
    int group_next = 0;
    int test_count = 0;
    vector<double> group(group_count);
    qDebug() << values_count;
    for(int i = 0; i < values_count; i++)
    {
        if(group_next == 0)
        {
            group_number++;
            if(i)
                out << "}	" << group_count << '\n';
            out << group_number << "	{";
            group_next = group_count;
            mera.paramList().first()->tx()->calc_poly_part(&group, group_count);
        }
        if(true)
        {
            if (group_next != group_count) //and i != values_count-1)
                out << ',';
            out << Qt::fixed << group[group_count - group_next];
            test_count++;
            group_next--;
        }
    }
    out << "}	" << group_count - group_next;
    file.close();
    qDebug() << test_count;
}

void MainWindow::Visual_poly()
{
    //QStandardItemModel
    const int cell_size = 20;
    const int points_count = 501;
    cerr << "test function";
    vector<double> points_x(points_count);
    vector<double> points_x2(points_count);
    double interv = apidecoding->boofer->data_x[1] - apidecoding->boofer->data_x[0];
    points_x[0] = apidecoding->boofer->data_x[0] - interv;
    points_x2[0] = apidecoding->boofer->data_x[0] - interv;
    double x_length = apidecoding->boofer->data_x[apidecoding->boofer->data_x.size() - 1] - points_x[0] + interv;
    double x_interval = x_length / (points_count - 1.0);
    for(int i = 1; i < points_count; i++)
    {
        points_x[i] = points_x[i - 1] + x_interval;
        points_x2[i] = points_x2[i - 1] + x_interval;
    }
    vector<double> points_y = apidecoding->calc_polynom(&res, &points_x);
    vector<double> res2 = apidecoding->legendre_approx_polynom(res.size());
    vector<double> points_y2 = apidecoding->calc_polynom(&res2, &points_x2);
    double max_y = apidecoding->boofer->data_y[0];
    double min_y = apidecoding->boofer->data_y[0];
    bool start = false;
    for(int i = 1; i < apidecoding->boofer->data_y.size(); i++)
    {
        if(max_y > apidecoding->boofer->data_y[i])
        {
            max_y = apidecoding->boofer->data_y[i];
        }
        else if(min_y < apidecoding->boofer->data_y[i])
        {
            min_y = apidecoding->boofer->data_y[i];
        }
    }
    for(int i = 1; i < points_count; i++)
    {
        if (not start)
        {
            if(points_x[i] >= apidecoding->boofer->data_x[0])
            {
                start = true;
            }
            continue;
        }
        if (points_x[i] > apidecoding->boofer->data_x[apidecoding->boofer->data_x.size() - 1])
            break;
        if(max_y > points_y[i])
        {
            max_y = points_y[i];
        }
        else if(min_y < points_y[i])
        {
            min_y = points_y[i];
        }
    }
    double diff = max_y - min_y;
    max_y += diff / 20.0;
    min_y -= diff / 20.0;
    double y_length = max_y - min_y;
    if (y_length == 0)
    {
        y_length = 1;
    }
    QPointF points[points_count];
    QPointF points2[points_count];
    double mult_x = (ui->pixmapLabel->width() - cell_size) / x_length;
    double mult_y = ui->pixmapLabel->height() / y_length;
    double shift_x = cell_size - points_x[0] * mult_x;
    double shift_y = -min_y * mult_y;
    for(int i = 0; i < points_count; i++)
    {
        points[i].setX(points_x[i] * mult_x + shift_x);
        points[i].setY((points_y[i] * mult_y + shift_y));
        points2[i].setX(points_x2[i] * mult_x + shift_x);
        points2[i].setY((points_y2[i] * mult_y + shift_y));
        //cerr << points[i].x() << " " << points[i].y() << "\n";
    }
    QPixmap pixmap(ui->pixmapLabel->width(), ui->pixmapLabel->height());
    pixmap.fill();
    QPainter painter(&pixmap);
    QPen pen2(Qt::red);

    pen2.setStyle(Qt::SolidLine);
    painter.setPen(pen2);
    painter.drawPolyline(points2, points_count);
    QPen pen3(Qt::blue);

    pen3.setStyle(Qt::SolidLine);
    painter.setPen(pen3);
    painter.drawPolyline(points, points_count);
    QPen pen(Qt::black);
    pen.setStyle(Qt::SolidLine);
    painter.setPen(pen);
    painter.setFont(QFont("Arial", 5));
    for (double x = cell_size; x <= ui->pixmapLabel->width() - cell_size; x += cell_size * 2)
    {
        painter.drawText(x, ui->pixmapLabel->height(), QString::number((x - shift_x) / mult_x));
    }
    for (double y = ui->pixmapLabel->height(); y > cell_size; y -= cell_size)
    {
        painter.drawText(0, y, QString::number((y - shift_y) / mult_y));
    }

    pen.setWidth(5);
    painter.setPen(pen);

    for (int i = 0; i < apidecoding->boofer->data_x.size(); i++)
    {
        painter.drawPoint(apidecoding->boofer->data_x[i] * mult_x + shift_x, (apidecoding->boofer->data_y[i] * mult_y + shift_y));
    }
    ui->pixmapLabel->setPixmap(pixmap);
}

void MainWindow::Visual(double *x_values, double *y_values, int input_size)
{
    input_size -= 1;
    const int cell_size = 20;
    cerr << "test function";
    double max_y = y_values[0];
    double min_y = y_values[0];
    for(int i = 1; i < input_size; i++)
    {
        if(max_y > y_values[i])
        {
            max_y = y_values[i];
        }
        else if(min_y < y_values[i])
        {
            min_y = y_values[i];
        }
    }
    double diff = max_y - min_y;
    max_y += diff / 20.0;
    min_y -= diff / 20.0;
    double y_length = max_y - min_y;
    double x_length = x_values[input_size - 1] - x_values[0];
    if (y_length == 0)
    {
        y_length = 1;
    }

    double mult_x = (ui->pixmapLabel->width() - cell_size) / x_length;
    double mult_y = ui->pixmapLabel->height() / y_length;
    double shift_x = cell_size - x_values[0] * mult_x;
    double shift_y = -min_y * mult_y;
    QPixmap pixmap(ui->pixmapLabel->width(), ui->pixmapLabel->height());
    pixmap.fill();
    QPainter painter(&pixmap);
    QPen pen(Qt::black);

    pen.setStyle(Qt::SolidLine);
    painter.setPen(pen);
    double prevX = x_values[0] * mult_x + shift_x;
    double prevY = y_values[0] * mult_y + shift_y;
    for(int i = 1; i < input_size; i++)
    {
        double curX = x_values[i] * mult_x + shift_x;
        double curY = y_values[i] * mult_y + shift_y;
        painter.drawLine(QPoint(prevX, prevY), QPoint(curX, curY));
        prevX = curX;
        prevY = curY;
    }
    QPen pen2(Qt::green);
    painter.setPen(pen2);
    for(int i = 0; i < input_size; i++)
    {
        painter.drawPoint(x_values[i] * mult_x + shift_x, y_values[i] * mult_y + shift_y);
    }
    painter.setFont(QFont("Arial", 5));
    for (double x = cell_size; x <= ui->pixmapLabel->width() - cell_size; x += cell_size * 2)
    {
        painter.drawText(x, ui->pixmapLabel->height(), QString::number((x - shift_x) / mult_x));
    }
    for (double y = ui->pixmapLabel->height(); y > cell_size; y -= cell_size)
    {
        painter.drawText(0, y, QString::number((y - shift_y) / mult_y));
    }

    pen.setWidth(5);
    painter.setPen(pen);

    for (int i = 0; i < apidecoding->boofer->data_x.size(); i++)
    {
        painter.drawPoint(apidecoding->boofer->data_x[i] * mult_x + shift_x, (apidecoding->boofer->data_y[i] * mult_y + shift_y));
    }
    ui->pixmapLabel->setPixmap(pixmap);
}


void MainWindow::Visual_mera()
{
    QGraphicsView *view = new QGraphicsView();
    view->setAttribute(Qt::WA_DeleteOnClose);

    view->resize(600,600);

    QGraphicsScene *scene = new QGraphicsScene(view);
    view->setScene(scene);

    GraphItemObject *test = new GraphItemObject(0);
    scene->addItem(test);
    view->show();
}

void MainWindow::make_test_data(int count, double step, double shift)
{
    apidecoding->clear_boofer();
    GH_model = new GHModel(apidecoding->boofer, this);
    GH_model->setHeader({"сопротивление", "градусы"});
    ui->inputTableView->setModel(GH_model);
    QRandomGenerator64 rd;
    for(double i = 0; i / step < count; i += step)
    {
        double bug = std::generate_canonical<qreal, std::numeric_limits<qreal>::digits>(rd);
        bug = (0.5 - bug) * 2 * shift / 100;
        GH_model->push_back_pair(i, sin(i) + bug);
    }
}

void MainWindow::open_file_dialog()
{
    open_file(QFileDialog::getOpenFileName(this, tr("Open File"), "/home", tr("Text files (*.tx*)")));
}

void MainWindow::save_to_file()
{
    QString filePath = QFileDialog::getOpenFileName(this, tr("Open File"), "/home", tr("Text files (*.tx1)"));
    if(filePath.isEmpty())
    {
        return;
    }
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QMessageBox::warning(this, "Внимание", "Файл не может быть открыт");
        return;
    }
    QTextStream out(&file);
    for(int i = 0; i < res.size(); i++)
    {
        out << res[i] << "\n";
    }
    file.close();
}

void MainWindow::open_info()
{
    //version
    QString str = "Version ";
    str.append(VERSION_STRING);
    info = new QLabel;
    info->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
    info->setText(str);
    info->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
    info->resize(200, 100);
    info->setWindowTitle(QObject::tr("о прграмме"));
    info->show();
}

void MainWindow::open_file_tree(const QModelIndex &index)
{
    open_file(fyleSystemModel->filePath(index));
}

void MainWindow::change_filter_file_systemModel(const QString &line)
{
    fyleSystemModel->setNameFilterDisables(false);
    QRegExp rx("\\((.*)\\)");
    QStringList filter_list = rx.capturedTexts();
    fyleSystemModel->setNameFilters(filter_list);
}

void MainWindow::add_data_line()
{
    int row = ui->inputTableView->selectionModel()->currentIndex().row();
    if (row == -1)
        GH_model->insertRows(GH_model->rowCount(), 1);
    else
        GH_model->insertRows(row + 1, 1);

}

void MainWindow::remove_data_line()
{
    int row = ui->inputTableView->selectionModel()->currentIndex().row();
    if (row == -1)
        GH_model->removeRows(GH_model->rowCount() -1, 1);
    else
        GH_model->removeRows(row, 1);
}

void MainWindow::to_clipboard()
{
    QItemSelectionModel *selection = ui->inputTableView->selectionModel();
    QModelIndexList indexes = selection->selectedIndexes();
    QClipboard *clipboard = QApplication::clipboard();
    QString gh;
    double last;
    double next;
    if(indexes.size())
    {
        last = indexes[0].column();
        gh = GH_model->data(indexes[0], Qt::DisplayRole).toString();
    }
    else
        return;
    for (int i = 1; i < indexes.size(); i++)
    {
        next = indexes[i].column();
        if(next > last)
            gh += "	";
        else
            gh += "\n";
        gh += GH_model->data(indexes[i], Qt::DisplayRole).toString();
        last = next;
    }
    clipboard->setText(gh);
}

void MainWindow::from_clipboard()
{
    QClipboard *clipboard = QApplication::clipboard();
    QString text = clipboard->text();
    QStringList rows = text.split("\n");
    QItemSelectionModel *selection = ui->inputTableView->selectionModel();
    QModelIndexList indexes = selection->selectedIndexes();
    if(!rows.size())
        return;
    int columns_size = rows[0].split("	").size();
    int rows_size = rows.size();
    if(columns_size > 2)
        return;
    QVector<QVector<double>> cells(rows_size);
    for(int i = 0; i < rows_size; i++)
    {
        QStringList columns = rows[i].split("	");
        if(columns.size() != columns_size)
            return;
        cells[i].resize(columns_size);
        for(int j = 0; j < columns_size; j++)
        {
            bool doubling;
            double cell = columns[j].toDouble(&doubling);
            if(!doubling)
                return;
            cells[i][j] = cell;
        }
    }
    if(!indexes.size())
    {
        if(columns_size != 2)
            return;
        for(int i = 0; i < rows_size; i++)
        {
            GH_model->push_back_pair(cells[i][0], cells[i][1]);
        }
    }
    if(columns_size * rows_size != indexes.size())
        return;
    for(int i = 0; i < rows_size; i++)
    {
        for(int j = 0; j < columns_size; j++)
        {
            GH_model->setData(indexes[j + columns_size * i], cells[i][j]);
        }
    }
}


void MainWindow::build_file_system_model()
{
    fyleSystemModel = new QFileSystemModel;
    fyleSystemModel->setRootPath("/");
    ui->treeView->setModel(fyleSystemModel);
    ui->treeView->show();
}

void MainWindow::build_GHmodel()
{
    GH_model = new GHModel(apidecoding->boofer, this);
    GH_model->setHeader({"сопротивление", "градусы"});
    ui->inputTableView->setModel(GH_model);
    ui->inputTableView->addAction(ui->copyAction);
    ui->inputTableView->addAction(ui->insertAction);
    ui->inputTableView->setContextMenuPolicy(Qt::ActionsContextMenu);
}



void MainWindow::open_file(QString filePath)
{
    if(filePath.isEmpty())
    {
        return;
    }
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QMessageBox::warning(this, "Внимание", "Файл не может быть открыт");
        return;
    }

    apidecoding->clear_boofer();
    int line_number = 1;
    while (!file.atEnd())
    {
        if (!line_to_boofer(file.readLine(), line_number++))
        {
            file.close();
            return;
        }
    }
    file.close();
    GH_model = new GHModel(apidecoding->boofer, this);
    GH_model->setHeader({"сопротивление", "градусы"});
    ui->inputTableView->setModel(GH_model);
}

bool MainWindow::line_to_boofer(QString line, int row_number)
{
    QStringList pair = line.split(QRegExp("[ 	]"));
    if(pair.length() != 2)
    {
        QMessageBox *error_question = new QMessageBox(this);
        if (error_question->question(this, "Внимание", QString("Нарушение структуры записи в файле. Ошибка в строке №%1."
                                                               " \n Продолжить чтение файла?").arg(row_number)) == QMessageBox::Yes)
        {
            return true;
        }
        return false;
    }
    bool ok_x;
    bool ok_y;
    double x = pair[0].toDouble(&ok_x);
    double y = pair[1].toDouble(&ok_y);
    if (!(ok_x && ok_y))
    {
        QMessageBox *error_question = new QMessageBox(this);
        if (error_question->question(this, "Внимание", QString("Не удаётся прочитать число. Ошибка в строке №%1."
                                                               " \n Продолжить чтение файла?").arg(row_number)) == QMessageBox::Yes)
        {
            return true;
        }
        return false;
    }
    apidecoding->add_to_boofer(x,y);
    return true;
}

void MainWindow::boofer_to_polynom()
{   bool ok;
    if(ui->condition_box->currentText() == "Допустимая погрешность %")
    {
        double mistake = ui->argument_edit->text().toDouble(&ok);
        if(!ok || mistake < 0)
        {
            QMessageBox *error = new QMessageBox(this);
            error->information(this, "Внимание", "Допустимы только не отрицательные числа");
            return;
        }
        res = apidecoding->least_squares_with_mistake(mistake);
    }
    else if(ui->condition_box->currentText() == "Степень полинома")
    {
        //bool ok;
        int max_pow = ui->argument_edit->text().toInt(&ok);
        if(!ok || max_pow < 1)
        {
            QMessageBox *error = new QMessageBox(this);
            error->information(this, "Внимание", "Допустимы только целые положительные числа");
            return;
        }
        res = apidecoding->least_squares_polynom(max_pow);
    }
    ui->mistake_label->setText(QString("%1%").arg(apidecoding->approx_mistake(&res), 0, 'g', 5));
    ui->outputTableWidget->setColumnCount(1);
    ui->outputTableWidget->setRowCount(res.size());
    for(int i = 0; i < res.size(); i++)
    {
        QTableWidgetItem *argItem = new QTableWidgetItem(QString("%1").arg(res[i]));
        ui->outputTableWidget->setItem(i, 0, argItem);
        QTableWidgetItem *headItem = new QTableWidgetItem(QString("%1").arg(i));
        ui->outputTableWidget->setVerticalHeaderItem(i, headItem);
    }
    QTableWidgetItem *newItem = new QTableWidgetItem("Полином");
    ui->outputTableWidget->setHorizontalHeaderItem(0, newItem);
}
